﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Text;
using System.IO;
using System.Web.Script.Serialization;
using NLog;
using Gollie.Mail;
using System.Threading;
using System.Threading.Tasks;
using MySQL.Db;

namespace Gollie.Ajax
{
	/// <summary>
	/// Summary description for WebMethods
	/// </summary>
	[WebService(Namespace = "http://www.gollie.nl/")]
	[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
	[System.ComponentModel.ToolboxItem(false)]
	// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
	[System.Web.Script.Services.ScriptService]
	public class WebMethods : System.Web.Services.WebService
	{
		protected static Logger logger = LogManager.GetCurrentClassLogger();

		[WebMethod]
		public AjaxResult SendGollieCode(string token)
		{
			AjaxArg arg = VerifyAjax<AjaxArg>(token, true);

			AjaxResult ajaxResult = new AjaxResult(arg);
			try
			{
				if (ajaxResult.ErrorCode == 0)
				{
					UserInfo userInfo = UserInfo.GetUserInfo(Convert.ToInt32(arg.tokenUser));

					MailClient.Queue(new EMail
					{
						Name = "resendqrcode",
						Recipients = userInfo.Email,
						AttachmentPath = userInfo.QRCodePath,
						Args = new Dictionary<string, string>()
						{
													{ "{aanhef}", userInfo.MailAanhef },
										    
						}
					});
				}
			}
			catch (Exception e)
			{
				if (ajaxResult.ErrorCode == 0)
					ajaxResult.ErrorCode = 99;

				ajaxResult.ErrorMessage = e.Message;

				logger.ErrorException("SendGollieCode error: ", e);
			}
			return ajaxResult;
		}

		[WebMethod]
		public NewPassword NewPassword(string token)
		{
			NewPasswordArg arg = VerifyAjax<NewPasswordArg>(token, true);

			return new NewPassword(arg);
		}

		[WebMethod]
		public PaymentInfo InitPayment(string token)//string userId, string cashierId)
		{
			InitPaymentArg arg = VerifyAjax<InitPaymentArg>(token, true);
			AjaxResult ajaxResult = new AjaxResult(arg);
			if (ajaxResult.ErrorCode != 0)
				return new PaymentInfo { ErrorCode = ajaxResult.ErrorCode, ErrorMessage = ajaxResult.ErrorMessage };

			var paymentInfo = PaymentInfo.InitPayment(arg.consumerId, arg.tokenUser);

			return paymentInfo;
		}

		[WebMethod]
		public AjaxResult Pay(string token)
		{
			PayArg arg = VerifyAjax<PayArg>(token, true);

			AjaxResult ajaxResult = new AjaxResult(arg);
			if (ajaxResult.ErrorCode != 0)
				return ajaxResult;

			try
			{
				string suserId = arg.suserId;
				string scashierId = arg.tokenUser;
				string ticket = arg.ticket;
				string samount = arg.samount;
				string scredit = arg.scredit;
				string pw = arg.pw;
				string email = arg.email;

				uint customerId = Hash.Decrypt(suserId);

				if (customerId == uint.MaxValue)
				{
					throw new Exception("Foutieve Gollie-code");
				}

				int cashierId = Convert.ToInt32(scashierId);

				PaymentTicket pbTicket = new PaymentTicket(ticket);

				if (pbTicket.IsValid(customerId, (uint)cashierId) == false)
				{
					ajaxResult.ErrorCode = 10;
					throw new Exception("Transactie verlopen. Start de procedure opnieuw.");
				}

				if (string.IsNullOrWhiteSpace(samount))
				{
					ajaxResult.ErrorCode = 11;
					throw new Exception("Geen bedrag ingevuld");
				}

				decimal amount = Helper.GetDecimal(samount) / 100;
				decimal credit = Helper.GetDecimal(scredit) / 100;

				if (credit < amount)
				{
					ajaxResult.ErrorCode = 12;
					throw new Exception(string.Format("Het beschikbare tegoed van {0:C} is kleiner dan het verschuldigde bedrag.\\r\\n>U kunt dit bedrag invullen en de klant verzoeken het restant van {1:C} op een andere wijze te voldoen.", credit, amount - credit));
				}

				string wwHash = Hash.GetHash(pw);

				//Both are cached for sure! Already got them from InitPayment
				UserInfo customerInfo = Database.Queries.GetUser((int)customerId, true);
				UserInfo cashierInfo = Database.Queries.GetUser(cashierId, true);

				logger.Info("Initializing payment of {0:C}, from {1} to {2}...", amount, customerInfo.FullName, cashierInfo.VendorInfo.BedrijfsNaam);
				Database.Queries.Pay(customerInfo.Id, cashierInfo.VendorId, amount, cashierInfo.Id, wwHash);
				DateTime payDate = DateTime.Now;
				logger.Info("Succes!");

				if (email == "1")
				{
					Task.Run( () => {SendConfirmationMail(customerInfo, cashierInfo, amount, payDate);});
				}
			}
			catch (MySql.Data.MySqlClient.MySqlException sqlEx)
			{
				ajaxResult.ErrorCode = sqlEx.Number;
				ajaxResult.ErrorMessage = MySqlErrorCodes.GetInstance().GetMessage(sqlEx);

				logger.ErrorException("Payment database error: ", sqlEx);

				if (sqlEx.Number == 255)
					Global.HackAttempt();
			}
			catch (TicketException te)
			{
				if (ajaxResult.ErrorCode == 0)
					ajaxResult.ErrorCode = 99;

				ajaxResult.ErrorMessage = te.Message;

				logger.ErrorException("Payment error, ticket exception ", te);
			}
			catch (Exception e)
			{
				if (ajaxResult.ErrorCode == 0)
					ajaxResult.ErrorCode = 99;

				ajaxResult.ErrorMessage = e.Message;

				logger.ErrorException("Payment error: ", e);
			}

			return ajaxResult;
		}

		internal static void SendConfirmationMail(UserInfo customerInfo, UserInfo cashierInfo, decimal amount, DateTime payDate)
		{
			Ticket reviewTicket = new Ticket();
			reviewTicket.Push(cashierInfo.VendorId);
			reviewTicket.Push(customerInfo.Id);
			reviewTicket.Push(payDate);
			reviewTicket.PushHash();

			string sAmount = ObjectFormatter.GetCurrencyString(amount);

			MailClient.Queue(new EMail
			{
				Name = "payed",
				Recipients = customerInfo.Email,
				Args = new Dictionary<string, string>()
				{
											{ "{amount}", sAmount },
											{ "{aanhef}", customerInfo.MailAanhef },
											{ "{vendorname}", cashierInfo.VendorInfo.BedrijfsNaam },
											{ "{vendorstreet}", cashierInfo.VendorInfo.Straat },
											{ "{vendorcity}", cashierInfo.VendorInfo.Plaats },
											{ "{timestamp}", ObjectFormatter.GetFormattedTimeStamp(payDate) },
											{ "{reviewurl}", ConfigAppSettings.GetValue("ReviewUrl") },
											{ "{reviewticket}", HttpUtility.UrlEncode(reviewTicket.ToString()) }
										    
				}
			});

		}

		/*
		private decimal GetDecimal(string amount)
		{
			string s = amount.Replace('.', ',');
			int commaCount = s.Count(p => p == ',');
			if (commaCount > 1)
				throw new FormatException("Incorrect bedrag");

			decimal val;
			if (decimal.TryParse(s, out val) == false)
				throw new FormatException("Incorrect bedrag");

			return val;
		}
		*/
		[WebMethod]
		public VendorLogin GetVendorLogin(string token)
		{
			UserLoginArg arg = VerifyAjax<UserLoginArg>(token, false);

			return new VendorLogin(arg);
		}

		[WebMethod]
		public UserLogin GetUserLogin(string token)
		{
			UserLoginArg arg = VerifyAjax<UserLoginArg>(token, false);

			return new UserLogin(arg);
		}

		[WebMethod (EnableSession=true)]
		public WebUserLogin GetWebUserLogin(string token)
		{
			//Task.Delay(2000).Wait();
			UserLoginArg arg = VerifyAjax<UserLoginArg>(token, false);

			return new WebUserLogin(arg);
		}

		[WebMethod]
		public ResetWW ResetPW(string token)
		{
			AjaxArg arg = VerifyAjax<AjaxArg>(token, false);

			return new ResetWW(arg);
		}

		[WebMethod]
		public UserSaldi GetUserSaldi(string token)
		{
			GetUserSaldiArg arg = VerifyAjax<GetUserSaldiArg>(token, true);
			
			return new UserSaldi(arg);
		}

		[WebMethod]
		public RegisterConsumer RegisterConsumer(string token)
		{
			RegisterConsumerArg arg = VerifyAjax<RegisterConsumerArg>(token, false);

			return new RegisterConsumer(arg);
		}

		[WebMethod(EnableSession = true)]
		public UserProfile GetUserProfile(string token)
		{
			UserProfileArg arg = VerifyAjax<UserProfileArg>(token, false);

			return new UserProfile(arg);
		}

		[WebMethod(EnableSession = true)]
		public VendorSettings GetVendorSettings(string token)
		{
			AjaxArg arg = VerifyAjax<AjaxArg>(token, false);

			return new VendorSettings(arg);
		}

		[WebMethod(EnableSession = true)]
		public UpdateUser UpdateUser(string token)
		{
			UpdateUserArg arg = VerifyAjax<UpdateUserArg>(token, true);

			return new UpdateUser(arg);
		}

		[WebMethod(EnableSession = true)]
		public UpdateVendorSettings UpdateVendorSettings(string token)
		{
			UpdateVendorSettingsArg arg = VerifyAjax<UpdateVendorSettingsArg>(token, true);

			return new UpdateVendorSettings(arg);
		}

		[WebMethod(EnableSession = true)]
		public NewVendorUser RegisterVendorUser(string token)
		{
			RegisterVendorUserArg arg = VerifyAjax<RegisterVendorUserArg>(token, true);

			return new NewVendorUser(arg);
		}

		//[WebMethod (EnableSession = true)]
		//public DialogHtml GetDialogHtml(string token)
		//{
		//	DialogHtmlArg arg = VerifyAjax<DialogHtmlArg>(token, false);

		//	return new DialogHtml(arg);
		//}

		[WebMethod]
		public AjaxResult InitKvK(string token)
		{
			AjaxArg arg = VerifyAjax<AjaxArg>(token, false);

			return new CompanyInfo(arg);
		}

		[WebMethod]
		public RegisterVendor RegisterVendor(string token)
		{
			RegisterVendorArg arg = VerifyAjax<RegisterVendorArg>(token, false);

			return new RegisterVendor(arg);
		}

		[WebMethod]
		public VendorGetCashClose GetLatestCashClose(string token)
		{
			AjaxArg arg = VerifyAjax<AjaxArg>(token, true);

			return new VendorGetCashClose(arg);
		}

		[WebMethod]
		public InsertCashClose InsertCashClose(string token)
		{
			InsertCashCloseArg arg = VerifyAjax<InsertCashCloseArg>(token, true);

			return new InsertCashClose(arg);
		}

		[WebMethod]
		public DeleteCashClose DeleteCashClose(string token)
		{
			DeleteCashCloseArg arg = VerifyAjax<DeleteCashCloseArg>(token, true);

			return new DeleteCashClose(arg);
		}

		[WebMethod]
		public InsertPendingPayment InsertPendingPayment(string token)
		{
			InsertPendingPaymentArg arg = VerifyAjax<InsertPendingPaymentArg>(token, true);

			return new InsertPendingPayment(arg);
		}

		[WebMethod]
		public PendingPayment GetPendingPayment(string token)
		{
			PendingPaymentArg arg = VerifyAjax<PendingPaymentArg>(token, true);

			return new PendingPayment(arg);
		}

		[WebMethod]
		public FinalizePendingPayment FinalizePendingPayment(string token)
		{
			FinalizePendingPaymentArg arg = VerifyAjax<FinalizePendingPaymentArg>(token, true);

			return new FinalizePendingPayment(arg);
		}

		[WebMethod]
		public CheckPendingPayment CheckPendingPayment(string token)
		{
			CheckPendingPaymentArg arg = VerifyAjax<CheckPendingPaymentArg>(token, true);

			return new CheckPendingPayment(arg);
		}

		private T VerifyAjax<T>(string arg, bool verifyToken) where T: AjaxArg
		{
			T obj;

			JavaScriptSerializer serializer = new JavaScriptSerializer();
			serializer.MaxJsonLength = 4 * 1024 * 1024;
			obj = serializer.Deserialize<T>(arg);

			if (verifyToken)
			{
				obj.VerifyToken();
				if (obj.tokenOk == false)
					logger.Error("Ajax call with illegal token for user {0}", obj.tokenUser);
			}

			return obj;
		}
	}

	public class InitPaymentArg : AjaxArg
	{
		public string consumerId;
	}

	public class PayArg : AjaxArg
	{
		public string ticket;
		public string suserId;
		public string samount;
		public string scredit;
		public string pw;
		public string email;
	}

	public class InsertPendingPaymentArg : AjaxArg
	{
		public string samount;
	}

	public class PendingPaymentArg : AjaxArg
	{
		public string vendorId;
		public string verificationCode;
	}

	public class FinalizePendingPaymentArg : AjaxArg
	{
		public string vendorId;
		public string verificationCode;
		public string pinCode;
		public string action; //0 = disapprove, 1=approve
		public string email; //1=send conformation
	}

	public class CheckPendingPaymentArg : AjaxArg
	{
		public string vendorId;
		public string verificationCode;
	}

	public class InsertCashCloseArg : AjaxArg
	{
		public string ww;
	}

	public class DeleteCashCloseArg : AjaxArg
	{
		public string ww;
	}

	public class NewPasswordArg : AjaxArg
	{
		public string username;
		public string ww;
	}

	public class UserLoginArg : AjaxArg
	{
		public string ww { get; set; }
	}

	public class UserProfileArg : AjaxArg
	{
		public string qrcode;
	}

	public class RegisterConsumerArg : AjaxArg
	{
		public string initials;
		public string infix;
		public string lastname;
		public string city;
		public string email;
		public string mobile;
		public string sex;
		public string birthdate;
		public string ww;
		public string pin;
	}

	public class RegisterVendorArg : AjaxArg
	{
		public string companyname;
		public string address;
		public string zipcode;
		public string country;
		public string website;
		public string initials;
		public string infix;
		public string lastname;
		public string city;
		public string email;
		public string mobile;
		public string sex;
		public string billingname;
		public string billingaccount;
		public string billingcountry;
		public string billingaddress;
		public string billingzipcode;
		public string billingcity;
		public string billingemail;
		public string logodata;
		public string logomimetype;
	}

	public class GetUserSaldiArg : AjaxArg
	{
	}

	public class UpdateUserArg : AjaxArg
	{
		public string initials;
		public string infix;
		public string lastname;
		public string city;
		public string email;
		public string mobile;
		public string sex;
		public string birthdate;
		public string newww;
		public string newwwrepeat;
		public string pin;
		public string pinrepeat;
		public string ww;
	}

	public class UpdateVendorSettingsArg : AjaxArg
	{
		public string allowrefund;
	}

	public class RegisterVendorUserArg : AjaxArg
	{
		public string initials;
		public string infix;
		public string lastname;
		public string email;
		public string mobile;
		public string sex;
		public string username;
		public string ww;
		public string role;
	}

	//public class DialogHtmlArg : AjaxArg
	//{
	//}

	public class AjaxArg
	{
		public string token { get; set; }
		public string tokenUser { get; set; }
		internal bool tokenOk = true;

		internal virtual void VerifyToken()
		{
			int id = Convert.ToInt32(tokenUser);
			UserInfo ui = UserInfo.GetUserInfo(id);

			tokenOk = (ui != null) && (ui.WebToken == token);
		}
	}
}
